package com.codeanalysissys.backend.javascript;

import com.codeanalysissys.backend.graphs.pdg.CDEdge;
import com.codeanalysissys.backend.graphs.pdg.ControlDependenceGraph;
import com.codeanalysissys.backend.graphs.pdg.PDNode;
import com.codeanalysissys.backend.javascript.parser.JavaScriptBaseVisitor;
import com.codeanalysissys.backend.javascript.parser.JavaScriptLexer;
import com.codeanalysissys.backend.javascript.parser.JavaScriptParser;
import ghaffarian.graphs.Edge;
import ghaffarian.nanologger.Logger;
import org.antlr.v4.runtime.ANTLRInputStream;
import org.antlr.v4.runtime.CommonTokenStream;
import org.antlr.v4.runtime.ParserRuleContext;
import org.antlr.v4.runtime.misc.Interval;
import org.antlr.v4.runtime.tree.ParseTree;
import org.apache.commons.lang3.tuple.MutablePair;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.Iterator;

public class JavaScriptCDGBuilder {

    public static ControlDependenceGraph build(InputStream inFile) throws IOException {
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        JavaScriptLexer lexer = new JavaScriptLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        JavaScriptParser parser = new JavaScriptParser(tokens);
        ParseTree tree = parser.program();
        ControlDependenceGraph cdg = new ControlDependenceGraph();
        ControlDependencyVisitor visitor = new ControlDependencyVisitor(cdg);
        visitor.visit(tree);
        return cdg;
    }

    public static ControlDependenceGraph build(File jsFile) throws IOException {
        System.out.println("current file: " + jsFile.getName());
        if (!jsFile.getName().endsWith(".js"))
            throw new IOException("Not a JavaScript File!");
        InputStream inFile = new FileInputStream(jsFile);
        ANTLRInputStream input = new ANTLRInputStream(inFile);
        JavaScriptLexer lexer = new JavaScriptLexer(input);
        CommonTokenStream tokens = new CommonTokenStream(lexer);
        JavaScriptParser parser = new JavaScriptParser(tokens);
        ParseTree tree = parser.program();
        Logger.debug("CTRL DEP ANALYSIS: " + jsFile.getPath());
        ControlDependenceGraph cdg = new ControlDependenceGraph(jsFile.getName());
        ControlDependencyVisitor visitor = new ControlDependencyVisitor(cdg);
        visitor.visit(tree);
        return cdg;
    }


    private static class ControlDependencyVisitor extends JavaScriptBaseVisitor<Void> {

        private ControlDependenceGraph cdg;
        private Deque<PDNode> ctrlDeps;
        private Deque<PDNode> negDeps;
        private Deque<Integer> jmpCounts;
        private Deque<PDNode> jumpDeps;
        private boolean buildRegion;
        private boolean follows;
        private int lastFollowDepth;
        private int regionCounter;
        private int jmpCounter;

        public ControlDependencyVisitor(ControlDependenceGraph cdg) {
            this.cdg = cdg;
            ctrlDeps = new ArrayDeque<>();
            negDeps = new ArrayDeque<>();
            jumpDeps = new ArrayDeque<>();
            jmpCounts = new ArrayDeque<>();
            buildRegion = false;
            follows = true;
            lastFollowDepth = 0;
            regionCounter = 1;
            jmpCounter = 0;
        }

        private void init() {
            ctrlDeps.clear();
            negDeps.clear();
            jumpDeps.clear();
            jmpCounts.clear();
            buildRegion = false;
            follows = true;
            lastFollowDepth = 0;
            regionCounter = 1;
            jmpCounter = 0;
        }

        @Override
        public Void visitImportStatement(JavaScriptParser.ImportStatementContext ctx) {
            PDNode importStmt = new PDNode();
            importStmt.setLineOfCode(ctx.getStart().getLine());
            importStmt.setCode(getOriginalCodeText(ctx));
            cdg.addVertex(importStmt);
            return null;
        }

        @Override
        public Void visitExportDeclaration(JavaScriptParser.ExportDeclarationContext ctx) {
            PDNode exportStmt = new PDNode();
            exportStmt.setLineOfCode(ctx.getStart().getLine());
            exportStmt.setCode(getOriginalCodeText(ctx));
            cdg.addVertex(exportStmt);
            return null;
        }

        @Override
        public Void visitExportDefaultDeclaration(JavaScriptParser.ExportDefaultDeclarationContext ctx) {
            PDNode exportStmt = new PDNode();
            exportStmt.setLineOfCode(ctx.getStart().getLine());
            exportStmt.setCode(getOriginalCodeText(ctx));
            cdg.addVertex(exportStmt);
            return null;
        }

        @Override
        public Void visitVariableStatement(JavaScriptParser.VariableStatementContext ctx) {
            PDNode var = new PDNode();
            var.setLineOfCode(ctx.getStart().getLine());
            var.setCode(getOriginalCodeText(ctx));
            addNodeEdge(var);

            return null;
        }

        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public Void visitExpressionStatement(JavaScriptParser.ExpressionStatementContext ctx) {
            PDNode expr = new PDNode();
            expr.setLineOfCode(ctx.getStart().getLine());
            expr.setCode(getOriginalCodeText(ctx));
            addNodeEdge(expr);
            return null;
        }

//        @Override
//        public Void visitStatement(JavaScriptParser.StatementContext ctx) {
//            // statement ';'
//            PDNode stmt = new PDNode();
//            stmt.setLineOfCode(ctx.getStart().getLine());
//            stmt.setCode(getOriginalCodeText(ctx));
//            addNodeEdge(stmt);
//            return null;
//        }

        @Override
        public Void visitIfStatement(JavaScriptParser.IfStatementContext ctx) {
            //    : If '(' expressionSequence ')' statement (Else statement)?

            // 创建if根节点
            PDNode ifNode = new PDNode();
            // 设置代码行号和代码内容
            ifNode.setLineOfCode(ctx.getStart().getLine());
            ifNode.setCode("if " + getOriginalCodeText(ctx.expressionSequence()));
            addNodeEdge(ifNode);

            // 创建thenRegion
            PDNode thenRegion = new PDNode();
            // 设置内容并与根节点建边，边类型为TRUE
            thenRegion.setLineOfCode(0);
            thenRegion.setCode("THEN");
            cdg.addVertex(thenRegion);
            cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.TRUE), thenRegion));

            // 创建elseRegion
            PDNode elseRegion = new PDNode();
            // 设置内容
            elseRegion.setLineOfCode(0);
            elseRegion.setCode("ELSE");

            // todo ctrlDep和negDeps区别作用？
            pushCtrlDep(thenRegion);
            negDeps.push(elseRegion);
            visit(ctx.statement(0));
            negDeps.pop();
            popCtrlDep(thenRegion);
            //
            if (ctx.statement().size() > 1) { // if with else
                follows = false;
                // 确认有else才加入cdg
                cdg.addVertex(elseRegion);
                cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
                //
                pushCtrlDep(elseRegion);
                negDeps.push(thenRegion);
                visit(ctx.statement(1));
                negDeps.pop();
                popCtrlDep(elseRegion);
            } else if (buildRegion) {
                // there is no else, but we need to add the ELSE region
                // todo 没有else也要加else region？
                cdg.addVertex(elseRegion);
                cdg.addEdge(new Edge<>(ifNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
            }
            follows = true;
            return null;
        }

        @Override
        public Void visitDoStatement(JavaScriptParser.DoStatementContext ctx) {
            // : Do statement While '(' expressionSequence ')' eos
            // 'do' statement 'while' parExpression ';'

            // 创建doRegion，因为没有代码含义所以是region
            PDNode doRegion = new PDNode();
            doRegion.setLineOfCode(ctx.getStart().getLine());
            doRegion.setCode("do");
            // 加入cfg并与ctrlDep头部结点相连
            addNodeEdge(doRegion);

            // 循环语句压入loopBlockDep
            pushLoopBlockDep(doRegion);
            // 访问子节点
            visit(ctx.statement());

            // the while-node is treated as the last statement of the loop
            // 创建whileNode
            PDNode whileNode = new PDNode();
            // 设置内容并加入cdg
            whileNode.setLineOfCode(ctx.expressionSequence().getStart().getLine());
            whileNode.setCode("while " + getOriginalCodeText(ctx.expressionSequence()));
            addNodeEdge(whileNode);

            // 处理完成后弹出
            popLoopBlockDep(doRegion);
            // this TRUE edge was removed, because only the repitition of
            // the block statements is dependent on the while-predicate
            // 因为只有块语句的重复依赖于while
            // cds.addEdge(whileNode, doRegion, new CDEdge(CDEdge.Type.TRUE));
            return null;
        }

        @Override
        public Void visitWhileStatement(JavaScriptParser.WhileStatementContext ctx) {
            //    | While '(' expressionSequence ')' statement                                                                              # WhileStatement
            // 'while' parExpression statement
            // 创建while结点
            PDNode whileNode = new PDNode();
            // 设置相关信息
            whileNode.setLineOfCode(ctx.getStart().getLine());
            whileNode.setCode("while " + getOriginalCodeText(ctx.expressionSequence()));
            // 加入cfg并与ctrlDep头部结点相连（与他爹相连）
            addNodeEdge(whileNode);

            // 创建loopRegion
            PDNode loopRegion = new PDNode();
            // 设置相关信息并与whileNode相连
            loopRegion.setLineOfCode(0);
            loopRegion.setCode("LOOP");
            cdg.addVertex(loopRegion);
            cdg.addEdge(new Edge<>(whileNode, new CDEdge(CDEdge.Type.TRUE), loopRegion));

            // 压入loopBlockDep处理循环体，处理完成后弹出（要弄谁就把谁入栈，然后处理儿子）
            pushLoopBlockDep(loopRegion);
            visit(ctx.statement());
            popLoopBlockDep(loopRegion);

            return null;
        }

        @Override
        public Void visitForStatement(JavaScriptParser.ForStatementContext ctx) {
            //    | For '(' (expressionSequence | variableDeclarationList)? ';' expressionSequence? ';' expressionSequence? ')' statement   # ForStatement
            //  First, we should check type of for-loop ...
            //  It's a traditional for-loop:
            //   forInit? ';' expression? ';' forUpdate?

            // 创建for头部三个部分对应结点
            PDNode forInit, forExpr, forUpdate;

            // 处理forInit的内容
            if (ctx.expressionSequence().size() == 3) {
                forInit = new PDNode();
                forInit.setLineOfCode(ctx.expressionSequence(0).getStart().getLine());
                forInit.setCode(getOriginalCodeText(ctx.expressionSequence(0)));
                // 跟爹连接
                addNodeEdge(forInit);
            }
            if (ctx.variableDeclarationList() != null && !ctx.variableDeclarationList().isEmpty()) {
                forInit = new PDNode();
                forInit.setLineOfCode(ctx.variableDeclarationList().getStart().getLine());
                forInit.setCode(getOriginalCodeText(ctx.variableDeclarationList()));
                // 跟爹连接
                addNodeEdge(forInit);
            }

            int updateIndex = 0;
            if (ctx.expressionSequence().size() == 3) {
                updateIndex++;
            }

            // 处理forExpr循环条件
            int forExprLine;
            String forExprCode;
            if (ctx.expressionSequence(updateIndex) == null) { // empty for-loop-predicate
                forExprCode = ";";
                forExprLine = ctx.getStart().getLine();
            } else {
                forExprCode = getOriginalCodeText(ctx.expressionSequence(updateIndex));
                forExprLine = ctx.expressionSequence(updateIndex).getStart().getLine();
            }
            forExpr = new PDNode();
            forExpr.setLineOfCode(forExprLine);
            forExpr.setCode("for (" + forExprCode + ")");
            // 跟爹连接
            addNodeEdge(forExpr);

            // 创建loopRegion，与forExpr相连
            PDNode loopRegion = new PDNode();
            loopRegion.setLineOfCode(0);
            loopRegion.setCode("LOOP");
            cdg.addVertex(loopRegion);
            cdg.addEdge(new Edge<>(forExpr, new CDEdge(CDEdge.Type.TRUE), loopRegion));

            // 处理循环体（准备处理儿子，把爹存起来）
            pushLoopBlockDep(loopRegion);
            visit(ctx.statement());
            if (ctx.expressionSequence(updateIndex + 1) != null) { // non-empty for-update
                forUpdate = new PDNode();
                forUpdate.setLineOfCode(ctx.expressionSequence(updateIndex + 1).getStart().getLine());
                forUpdate.setCode(getOriginalCodeText(ctx.expressionSequence(updateIndex + 1)));
                // we don't use 'addNodeEdge(forUpdate)' because the behavior of for-update
                // step is different from other statements with regards to break/continue.
                cdg.addVertex(forUpdate);
                cdg.addEdge(new Edge<>(ctrlDeps.peek(), new CDEdge(CDEdge.Type.EPSILON), forUpdate));
                System.out.println(ctrlDeps.peek());
            }
            // 处理完循环后搞定
            popLoopBlockDep(loopRegion);

            return null;
        }

        @Override
        public Void visitForInStatement(JavaScriptParser.ForInStatementContext ctx) {
            //   | For '(' (singleExpression | variableDeclarationList) In expressionSequence ')' statement                                # ForInStatement
            //  First, we should check type of for-loop ...
            //  This is a for-each loop;
            //   enhancedForControl:
            //     variableModifier* typeType variableDeclaratorId ':' expression

            PDNode forExpr = new PDNode();
            if (ctx.singleExpression() != null && !ctx.singleExpression().isEmpty()) {
                forExpr.setLineOfCode(ctx.singleExpression().getStart().getLine());
                forExpr.setCode("for (" + getOriginalCodeText(ctx.singleExpression()) + ctx.In().getText() + getOriginalCodeText(ctx.expressionSequence()) + ")");
            } else {
                // 若为列表且数量大于1则是语法错误
                forExpr.setLineOfCode(ctx.variableDeclarationList().getStart().getLine());
                if (ctx.variableDeclarationList().variableDeclaration().size() > 1)
                    forExpr.setCode("Syntax Error: for(" + getOriginalCodeText(ctx.variableDeclarationList()) + " in " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                else
                    forExpr.setCode("for(" + getOriginalCodeText(ctx.variableDeclarationList()) + " in " + getOriginalCodeText(ctx.expressionSequence()) + ")");
            }
            // 与爹连接
            addNodeEdge(forExpr);

            // 创建loopRegion
            PDNode loopRegion = new PDNode();
            loopRegion.setLineOfCode(0);
            loopRegion.setCode("LOOP");
            cdg.addVertex(loopRegion);
            cdg.addEdge(new Edge<>(forExpr, new CDEdge(CDEdge.Type.TRUE), loopRegion));

            // 处理循环体（先将爹存起来，处理完后移除）
            pushLoopBlockDep(loopRegion);
            visit(ctx.statement());
            popLoopBlockDep(loopRegion);

            return null;
        }

        @Override
        public Void visitForOfStatement(JavaScriptParser.ForOfStatementContext ctx) {
            //  | For Await? '(' (singleExpression | variableDeclarationList) identifier{this.p("of")}? expressionSequence ')' statement  # ForOfStatement
            PDNode forExpr = new PDNode();
            if (ctx.singleExpression() != null && !ctx.singleExpression().isEmpty()) {
                forExpr.setLineOfCode(ctx.singleExpression().getStart().getLine());
                forExpr.setCode("for (" + getOriginalCodeText(ctx.singleExpression()) + getOriginalCodeText(ctx.identifier()) + getOriginalCodeText(ctx.expressionSequence()) + ")");
            } else {
                forExpr.setLineOfCode(ctx.variableDeclarationList().getStart().getLine());
                // 若为列表且数量大于1则是语法错误
                if (ctx.variableDeclarationList().variableDeclaration().size() > 1)
                    forExpr.setCode("Syntax Error: for(" + getOriginalCodeText(ctx.variableDeclarationList()) + " of " + getOriginalCodeText(ctx.expressionSequence()) + ")");
                else
                    forExpr.setCode("for(" + getOriginalCodeText(ctx.variableDeclarationList()) + " of " + getOriginalCodeText(ctx.expressionSequence()) + ")");
            }
            addNodeEdge(forExpr);

            // 创建loopRegion
            PDNode loopRegion = new PDNode();
            loopRegion.setLineOfCode(0);
            loopRegion.setCode("LOOP");
            cdg.addVertex(loopRegion);
            cdg.addEdge(new Edge<>(forExpr, new CDEdge(CDEdge.Type.TRUE), loopRegion));

            // 处理循环体（先将爹存起来，处理完后移除）
            pushLoopBlockDep(loopRegion);
            visit(ctx.statement());
            popLoopBlockDep(loopRegion);
            return null;
        }

        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
//        @Override
//        public Void visitVarModifier(JavaScriptParser.VarModifierContext ctx) {
//            return visitChildren(ctx);
//        }

        @Override
        public Void visitContinueStatement(JavaScriptParser.ContinueStatementContext ctx) {
            //    : Continue ({this.notLineTerminator()}? identifier)? eos
            PDNode cnt = new PDNode();
            cnt.setLineOfCode(ctx.getStart().getLine());
            cnt.setCode(getOriginalCodeText(ctx));
            // 与爹相连
            addNodeEdge(cnt);
            // NOTE: an important assumption here is that 'continue'
            //       is the last statement inside an if-else body
            if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
                jumpDeps.push(negDeps.peek());
                jumpDeps.peek().setProperty("isExit", Boolean.FALSE);
                jumpDeps.peek().setProperty("isJump", Boolean.TRUE);
                lastFollowDepth = ctrlDeps.size();
                buildRegion = true;
            }
            return null;
        }

        @Override
        public Void visitBreakStatement(JavaScriptParser.BreakStatementContext ctx) {
            //: Break ({this.notLineTerminator()}? identifier)? eos
            PDNode brk = new PDNode();
            brk.setLineOfCode(ctx.getStart().getLine());
            brk.setCode(getOriginalCodeText(ctx));
            addNodeEdge(brk);
            //
            // Check for the special case of a 'break' inside a 'default' switch-block:
            if (!negDeps.isEmpty() && negDeps.peek().getCode().startsWith("default"))
                return null; // just ignore it, and do nothing!
            //
            // NOTE: an important assumption here is that 'break'
            //       is the last statement inside an if-else body,
            //       or it's the last statement inside a case-block
            if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
                jumpDeps.push(negDeps.peek());
                jumpDeps.peek().setProperty("isExit", Boolean.FALSE);
                jumpDeps.peek().setProperty("isJump", Boolean.TRUE);
                lastFollowDepth = ctrlDeps.size();
                buildRegion = true;
            }
            return null;
        }

        @Override
        public Void visitReturnStatement(JavaScriptParser.ReturnStatementContext ctx) {
            //    : Return ({this.notLineTerminator()}? expressionSequence)? eos
            PDNode ret = new PDNode();
            ret.setLineOfCode(ctx.getStart().getLine());
            ret.setCode(getOriginalCodeText(ctx));
            addNodeEdge(ret);
            // NOTE: an important assumption here is that 'return'
            //       is the last statement inside an if-else body
            //       or it's the last statement of the entire method
            if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
                jumpDeps.push(negDeps.peek());
                jumpDeps.peek().setProperty("isExit", Boolean.TRUE);
                jumpDeps.peek().setProperty("isJump", Boolean.FALSE);
                lastFollowDepth = ctrlDeps.size();
                buildRegion = true;
            }
            return visitChildren(ctx);
        }

        @Override
        public Void visitYieldStatement(JavaScriptParser.YieldStatementContext ctx) {
            //       : Yield ({this.notLineTerminator()}? expressionSequence)? eos
            PDNode ret = new PDNode();
            ret.setLineOfCode(ctx.getStart().getLine());
            ret.setCode(getOriginalCodeText(ctx));
            addNodeEdge(ret);
            // NOTE: an important assumption here is that 'yield'
            //       is the last statement inside an if-else body
            //       or it's the last statement of the entire method
            if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
                jumpDeps.push(negDeps.peek());
                jumpDeps.peek().setProperty("isExit", Boolean.TRUE);
                jumpDeps.peek().setProperty("isJump", Boolean.FALSE);
                lastFollowDepth = ctrlDeps.size();
                buildRegion = true;
            }
            return visitChildren(ctx);
        }

        @Override
        public Void visitWithStatement(JavaScriptParser.WithStatementContext ctx) {
            //    : With '(' expressionSequence ')' statement
            PDNode with = new PDNode();
            with.setLineOfCode(ctx.getStart().getLine());
            with.setCode("with (" + getOriginalCodeText(ctx.expressionSequence()) + ")");
            addNodeEdge(with);
            pushCtrlDep(with);
            visit(ctx.statement());
            popCtrlDep(with);

            return null;

//            PDNode ret = new PDNode();
//            ret.setLineOfCode(ctx.getStart().getLine());
//            ret.setCode(getOriginalCodeText(ctx));
//            addNodeEdge(ret);
//            // NOTE: an important assumption here is that 'with'
//            //       is the last statement inside an if-else body
//            //       or it's the last statement of the entire method
//            if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
//                jumpDeps.push(negDeps.peek());
//                jumpDeps.peek().setProperty("isExit", Boolean.TRUE);
//                jumpDeps.peek().setProperty("isJump", Boolean.FALSE);
//                lastFollowDepth = ctrlDeps.size();
//                buildRegion = true;
//            }
//            return visitChildren(ctx);
        }

        @Override
        public Void visitSwitchStatement(JavaScriptParser.SwitchStatementContext ctx) {
            //     : Switch '(' expressionSequence ')' caseBlock
            PDNode switchNode = new PDNode();
            switchNode.setLineOfCode(ctx.getStart().getLine());
            switchNode.setCode("switch " + getOriginalCodeText(ctx.expressionSequence()));
            addNodeEdge(switchNode);

            // 因为switch中有break因此压入loopBlockDep
            pushLoopBlockDep(switchNode);
            PDNode preNode = null, thenRegion = null;
            MutablePair<PDNode, PDNode> nodePair = new MutablePair<>(preNode, thenRegion);
            for (JavaScriptParser.CaseClausesContext caseClauses : ctx.caseBlock().caseClauses()) {
//                System.out.println(caseClauses.getText());
                for (JavaScriptParser.CaseClauseContext caseClause : caseClauses.caseClause()) {
//                    System.out.println("\t" + caseClause.getText());
                    visitCaseClause(caseClause, nodePair);
                }
//                visitCaseClauses(caseClauses);
            }
            // 处理default
//            visitCaseBlock(ctx.caseBlock());
            visitCaseClause(ctx.caseBlock().defaultClause(), nodePair);
            popLoopBlockDep(switchNode);

            return null;
        }

        public Void visitCaseClause(ParserRuleContext ctx, MutablePair<PDNode, PDNode> nodePair) {
            /**
             * 需要特殊处理的case：
             *      first case
             *      default
             */

            if (ctx == null)
                return null;

            PDNode preNode = nodePair.getLeft();
            PDNode thenRegion = nodePair.getRight();

            if (ctx instanceof JavaScriptParser.DefaultClauseContext) {
                PDNode defaultNode = new PDNode();
                defaultNode.setLineOfCode(ctx.getStart().getLine());
                defaultNode.setCode("default: ");
                JavaScriptParser.DefaultClauseContext defaultClause = (JavaScriptParser.DefaultClauseContext) ctx;
                addNodeEdge(defaultNode);
                if (defaultClause.statementList() != null && defaultClause.statementList().getChildCount() != 0) {
                    negDeps.push(defaultNode);
                    visit(defaultClause.statementList());
                    negDeps.pop();
                }

                return null;
            }

            JavaScriptParser.CaseClauseContext caseClause = (JavaScriptParser.CaseClauseContext) ctx;

            // 创建当前处理的case
            PDNode curNode = new PDNode();
            curNode.setLineOfCode(caseClause.getStart().getLine());
            System.out.println(caseClause.expressionSequence());
            curNode.setCode(caseClause.getChild(0).getText() + " " + caseClause.getChild(1).getText() + ":");
            cdg.addVertex(curNode);

            // 处理每个case与前面的关系
            if (preNode == null || preNode.getProperty("isEmpty") == null || !(Boolean) preNode.getProperty("isEmpty")) {
                // 若是第一个case，单独处理一下与switch根结点的连接
                // 若前面的case有内容，直接add
                addNodeEdge(curNode);
            } else {
                // 若存在preNode且preNode是一个空case
                cdg.addEdge(new Edge<>(preNode, new CDEdge(CDEdge.Type.FALSE), curNode));
            }

            // 处理每个case与后面的关系
            if (thenRegion == null) {
                // 若没有thenRegion则创建新的并加入cdg
                thenRegion = new PDNode();
                thenRegion.setLineOfCode(0);
                thenRegion.setCode("THEN");
                cdg.addVertex(thenRegion);
            }

            if (caseClause.statementList() != null && caseClause.statementList().getChildCount() != 0) {
                // 当前case下有语句
                // 设置当前结点属性
                curNode.setProperty("isEmpty", Boolean.FALSE);

                // 先将当前结点指向then再访问当前节点下的语句
                cdg.addEdge(new Edge<>(curNode, new CDEdge(CDEdge.Type.TRUE), thenRegion));

                PDNode elseRegion = new PDNode();
                elseRegion.setLineOfCode(0);
                elseRegion.setCode("ELSE");
                preNode = elseRegion;
                cdg.addVertex(elseRegion);

                pushCtrlDep(thenRegion);
                negDeps.push(elseRegion);
                visit(caseClause.statementList());
                negDeps.pop();
                popCtrlDep(thenRegion);
                thenRegion = null;

                // there is no else, but we need to add the ELSE region
                // todo 没有else也要加else region？
                if (buildRegion) {
                    // there was a 'break', so we need to keep the ELSE region
                    cdg.addEdge(new Edge<>(curNode, new CDEdge(CDEdge.Type.FALSE), elseRegion));
                } else if (cdg.getOutDegree(elseRegion) == 0)
                    // the ELSE region is not needed, so we remove it
                    cdg.removeVertex(elseRegion);
            } else {
                // 当前case下没有语句
                // 指向之前那个then
                // thenRegion在创建之时已经加入cdg，无需重复添加
                curNode.setProperty("isEmpty", Boolean.TRUE);
                cdg.addEdge(new Edge<>(curNode, new CDEdge(CDEdge.Type.TRUE), thenRegion));
                preNode = curNode;
            }

            nodePair.setLeft(preNode);
            nodePair.setRight(thenRegion);

            return null;
        }

//        public Void visitCaseClauses(JavaScriptParser.CaseClausesContext ctx) {
//            // 若一个case也没有
////            if (ctx == null || ctx.getChildCount() == 0) {
////                // 只处理default
////                PDNode defaultNode = new PDNode();
////                defaultNode.setLineOfCode(ctx.defaultClause().getStart().getLine());
////                defaultNode.setCode(getOriginalCodeText(ctx.defaultClause()));
////                addNodeEdge(defaultNode);
////                if (ctx.defaultClause().statementList() != null) {
////                    // todo 为何加到negDeps
////                    negDeps.push(defaultNode);
////                    visit(ctx.defaultClause().statementList());
////                    negDeps.pop();
////                }
////                return null;
////            }
//
//            // 处理ctx.caseClauses(0)第一个case
//            PDNode lastCase = new PDNode();
//            lastCase.setLineOfCode(ctx.caseClause(0).getStart().getLine());
//            lastCase.setCode(ctx.getChild(0).getText() + " " + ctx.getChild(1).getText() + ":");
//            addNodeEdge(lastCase);
//            PDNode thenRegion = null;
//
//            // 判断条件有误，应该是ctx.caseClauses(0)最后一个caseClause的statementList() != null
////            if (ctx.caseClauses(0).caseClause(0).statementList() != null) {
//            if (ctx.caseClause(ctx.getChildCount() - 1).statementList() != null) {
//                // 处理第一个case下的语句
//                thenRegion = new PDNode();
//                thenRegion.setLineOfCode(0);
//                thenRegion.setCode("THEN");
//                cdg.addVertex(thenRegion);
//                cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.TRUE), thenRegion));
//            }
//            // 遍历ctx.caseClauses(0)剩余case
//            for (int i = 1; i < ctx.caseClause().size(); i++) {
//                PDNode nextCase = new PDNode();
//                nextCase.setLineOfCode(ctx.caseClause(i).getStart().getLine());
//                nextCase.setCode(getOriginalCodeText(ctx.caseClause(i)));
//                cdg.addVertex(nextCase);
//
//                // 每个case的false边指向下一个case
//                cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), nextCase));
//                // 每个case的true边都指向同一个then
//                cdg.addEdge(new Edge<>(nextCase, new CDEdge(CDEdge.Type.TRUE), thenRegion));
//
//                // 迭代一下
//                lastCase = nextCase;
//            }
//
//            // 处理then和else的内容
//            if (ctx.caseClause(ctx.getChildCount() - 1).statementList() != null) {
//                PDNode elseRegion = new PDNode();
//                elseRegion.setLineOfCode(0);
//                elseRegion.setCode("ELSE");
//
//                // We have to add the ELSE here, just
//                // in case it is needed in the following.
//                cdg.addVertex(elseRegion);
//
//                // 此处的处理类似if-else
//                pushCtrlDep(thenRegion);
//                negDeps.push(elseRegion);
//                // 访问ctx.caseClauses(0)中最后一个case的statementList
//                visit(ctx.caseClause(ctx.getChildCount() - 1).statementList());
//                negDeps.pop();
//                popCtrlDep(thenRegion);
//
//                if (buildRegion) {
//                    // there was a 'break', so we need to keep the ELSE region
//                    cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), elseRegion));
//                } else if (cdg.getOutDegree(elseRegion) == 0)
//                    // the ELSE region is not needed, so we remove it
//                    cdg.removeVertex(elseRegion);
//            }
//
//            return null;
//        }

//        @Override
//        public Void visitCaseBlock(JavaScriptParser.CaseBlockContext ctx) {
//            //caseBlock
//            //    : '{' caseClauses? (defaultClause caseClauses?)? '}'
//
//            // 若一个case也没有
//            if (ctx.caseClauses() == null || ctx.caseClauses().size() == 0) {
//                // 只处理default
//                PDNode defaultNode = new PDNode();
//                defaultNode.setLineOfCode(ctx.defaultClause().getStart().getLine());
//                defaultNode.setCode(getOriginalCodeText(ctx.defaultClause()));
//                addNodeEdge(defaultNode);
//                if (ctx.defaultClause().statementList() != null) {
//                    // todo 为何加到negDeps
//                    negDeps.push(defaultNode);
//                    visit(ctx.defaultClause().statementList());
//                    negDeps.pop();
//                }
//                return null;
//            }
//
//            // 处理ctx.caseClauses(0)第一个case即整个switch的第一个case
//            PDNode lastCase = new PDNode();
//            lastCase.setLineOfCode(ctx.caseClauses(0).caseClause(0).getStart().getLine());
//            lastCase.setCode(getOriginalCodeText(ctx.caseClauses(0).caseClause(0)));
//            addNodeEdge(lastCase);
//            PDNode thenRegion = null;
//
//            // 判断条件有误，应该是ctx.caseClauses(0)最后一个caseClause的statementList() != null
////            if (ctx.caseClauses(0).caseClause(0).statementList() != null) {
//            if (ctx.caseClauses(0).caseClause(ctx.caseClauses(0).getChildCount() - 1).statementList() != null) {
//                // 处理第一个case下的语句
//                thenRegion = new PDNode();
//                thenRegion.setLineOfCode(0);
//                thenRegion.setCode("THEN");
//                cdg.addVertex(thenRegion);
//                cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.TRUE), thenRegion));
//            }
//            // 遍历ctx.caseClauses(0)剩余case
//            for (int i = 1; i < ctx.caseClauses(0).caseClause().size(); i++) {
//                PDNode nextCase = new PDNode();
//                nextCase.setLineOfCode(ctx.caseClauses(0).caseClause(i).getStart().getLine());
//                nextCase.setCode(getOriginalCodeText(ctx.caseClauses(0).caseClause(i)));
//                cdg.addVertex(nextCase);
//
//                // 每个case的false边指向下一个case
//                cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), nextCase));
//                // 每个case的true边都指向同一个then
//                cdg.addEdge(new Edge<>(nextCase, new CDEdge(CDEdge.Type.TRUE), thenRegion));
//
//                // 迭代一下
//                lastCase = nextCase;
//            }
//
//            // 处理then和else的内容
//            if (ctx.caseClauses(0).caseClause(ctx.caseClauses(0).getChildCount() - 1).statementList() != null) {
//                PDNode elseRegion = new PDNode();
//                elseRegion.setLineOfCode(0);
//                elseRegion.setCode("ELSE");
//
//                // We have to add the ELSE here, just
//                // in case it is needed in the following.
//                cdg.addVertex(elseRegion);
//
//                // 此处的处理类似if-else
//                pushCtrlDep(thenRegion);
//                negDeps.push(elseRegion);
//                // 访问ctx.caseClauses(0)中最后一个case的statementList
//                visit(ctx.caseClauses(0).caseClause(ctx.caseClauses(0).getChildCount() - 1).statementList());
//                negDeps.pop();
//                popCtrlDep(thenRegion);
//
//                if (buildRegion) {
//                    // there was a 'break', so we need to keep the ELSE region
//                    cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), elseRegion));
//                } else if (cdg.getOutDegree(elseRegion) == 0)
//                    // the ELSE region is not needed, so we remove it
//                    cdg.removeVertex(elseRegion);
//            }
//
//
////            for (int i = 1; i < ctx.caseClauses().size(); i++) {
////                for (JavaScriptParser.CaseClauseContext caseClauseContext : ctx.caseClauses(i).caseClause()) {
////                    PDNode nextCase = new PDNode();
////                    nextCase.setLineOfCode(caseClauseContext.getStart().getLine());
////                    nextCase.setCode(getOriginalCodeText(caseClauseContext));
////                    cdg.addVertex(nextCase);
////                    cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), nextCase));
////                    cdg.addEdge(new Edge<>(nextCase, new CDEdge(CDEdge.Type.TRUE), thenRegion));
////                }
////            }
////            if (ctx.defaultClause() != null) {
////                PDNode nextCase = new PDNode();
////                nextCase.setLineOfCode(ctx.defaultClause().getStart().getLine());
////                nextCase.setCode(getOriginalCodeText(ctx.defaultClause()));
////                cdg.addVertex(nextCase);
////                cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), nextCase));
////                cdg.addEdge(new Edge<>(nextCase, new CDEdge(CDEdge.Type.TRUE), thenRegion));
////            }
////            PDNode elseRegion = new PDNode();
////            elseRegion.setLineOfCode(0);
////            elseRegion.setCode("ELSE");
////            cdg.addVertex(elseRegion);
////            pushCtrlDep(thenRegion);
////            negDeps.push(elseRegion);
////            for (int i = 0; i < ctx.caseClauses().size(); i++) {
////                for (int j = 0; j < ctx.caseClauses(i).caseClause().size(); j++) {
////                    visit(ctx.caseClauses(i).caseClause(j).statementList());
////                }
////            }
////            if (ctx.defaultClause() != null) {
////                visit(ctx.defaultClause().statementList());
////            }
////            negDeps.pop();
////            popCtrlDep(thenRegion);
////            if (buildRegion) {
////                // there was a 'break', so we need to keep the ELSE region
////                cdg.addEdge(new Edge<>(lastCase, new CDEdge(CDEdge.Type.FALSE), elseRegion));
////            } else if (cdg.getOutDegree(elseRegion) == 0) {
////                // the ELSE region is not needed, so we remove it
////                cdg.removeVertex(elseRegion);
////            }
//            return null;
//        }

        @Override
        public Void visitLabelledStatement(JavaScriptParser.LabelledStatementContext ctx) {
            //    : identifier ':' statement
            PDNode labelRegion = new PDNode();
            labelRegion.setLineOfCode(ctx.getStart().getLine());
            labelRegion.setCode(ctx.identifier().Identifier() + ": ");
            addNodeEdge(labelRegion);
            pushCtrlDep(labelRegion);
            visit(ctx.statement());
            popCtrlDep(labelRegion);
            return null;
        }

        @Override
        public Void visitThrowStatement(JavaScriptParser.ThrowStatementContext ctx) {
            //    : Throw {this.notLineTerminator()}? expressionSequence eos
            PDNode thr = new PDNode();
            thr.setLineOfCode(ctx.getStart().getLine());
            thr.setCode(getOriginalCodeText(ctx));
            addNodeEdge(thr);
            // NOTE: an important assumption here is that 'throw'
            //       is the last statement inside an if-else body,
            //       or it's the last statement of a try-catch block,
            //       or it's the last statement of the entire method
            // throw使得代码去往另一个地方，即也有控制效果
            // 去往的那个地方即为当前语句的negDeps最新加入那个位置
            // 因此要判断negDeps是否为空。若negDeps不为空，表明有地方跳转去
            // todo ctrlDeps.size() >= lastFollowDepth ？
            if (!negDeps.isEmpty() && ctrlDeps.size() >= lastFollowDepth) {
                // 设置跳转node/region
                jumpDeps.push(negDeps.peek());
                // throw使得ctrlDeps的退出，并非跳转
                jumpDeps.peek().setProperty("isExit", Boolean.TRUE);
                jumpDeps.peek().setProperty("isJump", Boolean.FALSE);
                // todo 记录lastFollowDepth
                lastFollowDepth = ctrlDeps.size();
                // 存在跳转情况因此要
                buildRegion = true;
            }
            return null;
        }

        @Override
        public Void visitTryStatement(JavaScriptParser.TryStatementContext ctx) {
            //    : Try block (catchProduction finallyProduction? | finallyProduction)
            // 创建tryRegion，代表整个try
            PDNode tryRegion = new PDNode();
            // 设置代码行号、代码内容和属性
            tryRegion.setLineOfCode(ctx.getStart().getLine());
            tryRegion.setCode("try");
            tryRegion.setProperty("isTry", Boolean.TRUE);
            // 与爹相连
            addNodeEdge(tryRegion);

            // 压入ctrlDep准备处理儿子
            pushCtrlDep(tryRegion);
            // todo 为何要压入neg？
            negDeps.push(tryRegion);
            // 访问try下代码块
            visit(ctx.block());
            // visit any available catch clauses
            if (ctx.catchProduction() != null) {
                // 'catch' '(' variableModifier* catchType Identifier ')' block
                // 若存在catch，js中至多只有一个catch
                PDNode catchNode = new PDNode();
                catchNode.setLineOfCode(ctx.catchProduction().getStart().getLine());
                catchNode.setCode("catch(" + getOriginalCodeText(ctx.catchProduction().assignable()) + ")");
//                catchNode.setCode(ctx.catchProduction().getText());
                // 将catch节点加入cdg并与try建边
                cdg.addVertex(catchNode);
                cdg.addEdge(new Edge<>(tryRegion, new CDEdge(CDEdge.Type.THROWS), catchNode));
                // 压入ctrlDep，处理儿子；处理完成后弹出
                pushCtrlDep(catchNode);
                visit(ctx.catchProduction().block());
                popCtrlDep(catchNode);
            }
            // negDeps和ctrlDep弹出的顺序按照先进后出的顺序
            negDeps.pop(); // pop try-region
            popCtrlDep(tryRegion); // pop try-region

            // If there is a finally-block
            if (ctx.finallyProduction() != null) {
                // 'finally' block
                // 创建finallyRegion
                PDNode finallyRegion = new PDNode();
                // 设置代码行号和代码内容并与前面的爹建边
                // (非try，因为finally下的代码一定会执行，所以当作它和try共同的爹下的一个儿子即可)
                finallyRegion.setLineOfCode(ctx.finallyProduction().getStart().getLine());
                finallyRegion.setCode("finally");
                addNodeEdge(finallyRegion);
                // 将爹压入栈处理儿子，处理完成后弹出爹
                pushCtrlDep(finallyRegion);
                visit(ctx.finallyProduction().block());
                popCtrlDep(finallyRegion);
            }
            return null;
        }

        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public Void visitDebuggerStatement(JavaScriptParser.DebuggerStatementContext ctx) {
            PDNode debugger = new PDNode();
            debugger.setLineOfCode(ctx.getStart().getLine());
            debugger.setCode(getOriginalCodeText(ctx));
            addNodeEdge(debugger);

            return null;
        }

        @Override
        public Void visitFunctionDeclaration(JavaScriptParser.FunctionDeclarationContext ctx) {
            //    : Async? Function_ '*'? identifier '(' formalParameterList? ')' functionBody
            init();

            // 创建函数入口结点
            PDNode entry = new PDNode();
            entry.setLineOfCode(ctx.getStart().getLine());
            String args = ctx.formalParameterList() != null ? getOriginalCodeText(ctx.formalParameterList()) : "";
            entry.setCode(ctx.identifier().Identifier() + args);
            cdg.addVertex(entry);

            // 压入栈访问儿子
            pushCtrlDep(entry);
            if (ctx.functionBody() != null)
                visit(ctx.functionBody());

            // 访问结束后退出
            PDNode exit = new PDNode();
            exit.setLineOfCode(0);
            exit.setCode("exit");
            cdg.addVertex(exit);
            cdg.addEdge(new Edge<>(entry, new CDEdge(CDEdge.Type.EPSILON), exit));
            return null;
        }

        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
        @Override
        public Void visitClassDeclaration(JavaScriptParser.ClassDeclarationContext ctx) {
            PDNode classDecl = new PDNode();
            classDecl.setLineOfCode(ctx.getStart().getLine());
            classDecl.setCode("class " + getOriginalCodeText(ctx.identifier()));

            cdg.addVertex(classDecl);
            pushCtrlDep(classDecl);
            visitChildren(ctx);
            popCtrlDep(classDecl);

            return null;
        }

        /**
         * {@inheritDoc}
         *
         * <p>The default implementation returns the result of calling
         * {@link #visitChildren} on {@code ctx}.</p>
         */
//        @Override
//        public Void visitClassTail(JavaScriptParser.ClassTailContext ctx) {
//            return visitChildren(ctx);
//        }

//        @Override
//        public Void visitBlock(JavaScriptParser.BlockContext ctx) {
//            System.out.println(ctx.getText());
//            return super.visitBlock(ctx);
//        }

//        @Override
//        public Void visitClassElement(JavaScriptParser.ClassElementContext ctx) {
//            // : (Static | {this.n("static")}? identifier | Async)* (methodDefinition | assignable '=' objectLiteral ';')
//            //    | emptyStatement_
//            //    | '#'? propertyName '=' singleExpression
//            //    ;
//            init();
//            System.out.println(ctx.getText());
//            //
//            PDNode block = new PDNode();
//            boolean flag = false;
//            if (ctx.getChildCount() == 2 && ctx.getChild(0).getText().equals("static")) {
//                // 静态代码块
//                block.setLineOfCode(ctx.getStart().getLine());
//                block.setCode("static");
//            } else {
//                // 普通代码块
//                block.setLineOfCode(0);
//                block.setCode("block");
//            }
//            if (ctx.emptyStatement_() != null) {
//                block.setLineOfCode(ctx.getStart().getLine());
//                block.setCode(getOriginalCodeText(ctx.emptyStatement_()));
//                return null;
//            } else if (ctx.getChild(0).getText().equals("#")) {
//                // 私有属性
//                block.setLineOfCode(ctx.getStart().getLine());
//                block.setCode("#");
//            } else if (ctx.propertyName() != null) {
//                // 公共属性
//                block.setLineOfCode(ctx.getStart().getLine());
//                block.setCode(getOriginalCodeText(ctx.propertyName()) + "=" + getOriginalCodeText(ctx.singleExpression()));
//            } else {f
//                flag = true;
//                block.setLineOfCode(ctx.getStart().getLine());
//                StringBuffer stringBuffer = new StringBuffer();
//                stringBuffer.append("static ");
//                for (JavaScriptParser.IdentifierContext identifierContext : ctx.identifier()) {
//                    stringBuffer.append(getOriginalCodeText(identifierContext) + " ");
//                }
//                if (ctx.methodDefinition() != null && !ctx.methodDefinition().isEmpty()) {
//                    stringBuffer.append(getOriginalCodeText(ctx.methodDefinition()));
//                } else {
//                    stringBuffer.append(getOriginalCodeText(ctx.assignable()));
//                }
//                stringBuffer.append("=" + getOriginalCodeText(ctx.objectLiteral()) + ";");
//                block.setCode(stringBuffer.toString());
//            }
//            System.out.println(block);
//            cdg.addVertex(block);
//            pushCtrlDep(block);
//
//            //
//            PDNode exit = new PDNode();
//            exit.setLineOfCode(0);
//            exit.setCode("exit");
//            cdg.addVertex(exit);
//            cdg.addEdge(new Edge<>(block, new CDEdge(CDEdge.Type.EPSILON), exit));
//            return null;
//        }

        @Override
        public Void visitMethodDefinition(JavaScriptParser.MethodDefinitionContext ctx) {
            // : '*'? '#'? propertyName '(' formalParameterList? ')' functionBody
            //    | '*'? '#'? getter '(' ')' functionBody
            //    | '*'? '#'? setter '(' formalParameterList? ')' functionBody
//            init();

            // 方法入口
            PDNode entry = new PDNode();
            entry.setLineOfCode(ctx.getStart().getLine());
            String retType;
            if (ctx.propertyName() != null) {
                // 构造函数或普通函数
                String args = ctx.formalParameterList() != null ? getOriginalCodeText(ctx.formalParameterList()) : "";
                entry.setCode(getOriginalCodeText(ctx.propertyName()) + "(" + args + ")");
            } else if (ctx.getter() != null) {
                // getter
                entry.setCode(getOriginalCodeText(ctx.getter()) + "()");
            } else {
                // setter
                entry.setCode(getOriginalCodeText(ctx.setter()) + "()");
            }
            addNodeEdge(entry);
//            cdg.addVertex(entry);
            // 爹压入栈
            pushCtrlDep(entry);
            // 处理儿子
            if (ctx.functionBody() != null)
                visit(ctx.functionBody());
            popCtrlDep(entry);
            //
            PDNode exit = new PDNode();
            exit.setLineOfCode(0);
            exit.setCode("exit");
            cdg.addVertex(exit);
            cdg.addEdge(new Edge<>(entry, new CDEdge(CDEdge.Type.EPSILON), exit));


            return null;
        }


        /**
         * Add given node to the CD-subgraph and
         * create a new CD-edge based on the last control-dependency.
         */
        private void addNodeEdge(PDNode node) {
            checkBuildFollowRegion();
            cdg.addVertex(node);
            cdg.addEdge(new Edge<>(ctrlDeps.peek(), new CDEdge(CDEdge.Type.EPSILON), node));
        }

        /**
         * Check if a follow-region must be created;
         * if so, create it and push it on the CTRL-dependence stack.
         */
        private void checkBuildFollowRegion() {
            Logger.debug("FOLLOWS = " + follows);
            Logger.debug("BUILD-REGION = " + buildRegion);
            if (buildRegion && follows) {
                PDNode followRegion = new PDNode();
                followRegion.setLineOfCode(0);
                followRegion.setCode("FOLLOW-" + regionCounter++);
                cdg.addVertex(followRegion);
                // check to see if there are any exit-jumps in the current chain
                followRegion.setProperty("isJump", Boolean.TRUE);
                for (PDNode dep : jumpDeps)
                    if ((Boolean) dep.getProperty("isExit")) {
                        followRegion.setProperty("isJump", Boolean.FALSE);
                        followRegion.setProperty("isExit", Boolean.TRUE);
                    }
                if ((Boolean) followRegion.getProperty("isJump"))
                    ++jmpCounter;
                // connect the follow-region
                if (Boolean.TRUE.equals(jumpDeps.peek().getProperty("isTry"))) {
                    PDNode jmpDep = jumpDeps.pop();
                    if (!cdg.containsVertex(jmpDep))
                        cdg.addVertex(jmpDep);
                    cdg.addEdge(new Edge<>(jmpDep, new CDEdge(CDEdge.Type.NOT_THROWS), followRegion));
                } else {
                    PDNode jmpDep = jumpDeps.pop();
                    if (!cdg.containsVertex(jmpDep))
                        cdg.addVertex(jmpDep);
                    cdg.addEdge(new Edge<>(jmpDep, new CDEdge(CDEdge.Type.EPSILON), followRegion));
                }
                // if the jump-chain is not empty, remove all non-exit jumps
                if (!jumpDeps.isEmpty()) {
                    for (Iterator<PDNode> itr = jumpDeps.iterator(); itr.hasNext(); ) {
                        PDNode dep = itr.next();
                        if (Boolean.FALSE.equals(dep.getProperty("isExit")))
                            itr.remove();
                    }
                }
                lastFollowDepth = 0;
                pushCtrlDep(followRegion);
            }
        }

        /**
         * Push given node to the control-dependency stack.
         */
        private void pushCtrlDep(PDNode dep) {
            ctrlDeps.push(dep);
            buildRegion = false;
        }

        /**
         * Push this loop block region to the control-dependency stack
         * and reset the jumps-counter for this loop-block.
         */
        private void pushLoopBlockDep(PDNode region) {
            pushCtrlDep(region);
            jmpCounts.push(jmpCounter);
            jmpCounter = 0;
        }

        /**
         * Pop out the last dependency off the stack and
         * set the 'buildRegion' flag if necessary.
         */
        private void popCtrlDep(PDNode dep) {
            ctrlDeps.remove(dep); //ctrlDeps.pop();
            buildRegion = !jumpDeps.isEmpty();
        }

        /**
         * Pop out this loop-block region off the control stack
         * and also pop off all jump-dependencies of this block.
         */
        private void popLoopBlockDep(PDNode region) {
            for (Iterator<PDNode> itr = ctrlDeps.iterator(); jmpCounter > 0 && itr.hasNext(); ) {
                // NOTE: This iteration works correctly, even though ctrlDeps is a stack.
                //       This is due to the Deque implementation, which removes in LIFO.
                PDNode dep = itr.next();
                if (Boolean.TRUE.equals(dep.getProperty("isJump"))) {
                    itr.remove();
                    --jmpCounter;
                }
            }
            jmpCounter = jmpCounts.pop();
            lastFollowDepth = 0;
            popCtrlDep(region);
        }

        /**
         * Get the original program text for the given parser-rule context.
         * This is required for preserving whitespaces.
         */
        private String getOriginalCodeText(ParserRuleContext ctx) {
            int start = ctx.start.getStartIndex();
            int stop = ctx.stop.getStopIndex();
            Interval interval = new Interval(start, stop);
            return ctx.start.getInputStream().getText(interval);
        }

    }
}